package com.immortal.core.filters;

import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.immortal.core.filters.wrapper.RequestWrapper;
import com.immortal.core.filters.wrapper.ResponseWrapper;
import io.micrometer.core.instrument.MeterRegistry;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.Ordered;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingResponseWrapper;
import org.springframework.web.util.WebUtils;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;

/**
 * <p>请求日志打印
 *
 * @author wind
 * @version 1.0
 * @since 2018/12/12
 */
@Slf4j
public class HttpTraceLogFilter extends OncePerRequestFilter implements Ordered {
    private final MeterRegistry registry;
    private AntPathMatcher antPathMatcher = new AntPathMatcher();
    private List<String> allow = new ArrayList<>();
    private List<String> expose = new ArrayList<>();
    public HttpTraceLogFilter(MeterRegistry registry) {
        this.registry = registry;
    }

    public void setTraceLogService(HttpTraceLogService traceLogService) {
        this.traceLogService = traceLogService;
    }

    public void addAllowPatterns(String... antPatterns) {
        allow.add("/**");
        for (String s : antPatterns) {
            if (StringUtils.hasText(s)) {
                allow.add(s);
            }
        }
    }

    public void addExposePatterns(String... antPatterns) {
        for (String s : antPatterns) {
            if (StringUtils.hasText(s)) {
                expose.add(s);
            }
        }
    }

    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE - 10;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        if (!traceLogService.isRequestValid(request)) {
            filterChain.doFilter(request, response);
            return;
        }
//        if (!(request instanceof ContentCachingRequestWrapper)) {
//            request = new ContentCachingRequestWrapper(request);
//        }
//        if (!(response instanceof ContentCachingResponseWrapper)) {
//            response = new ContentCachingResponseWrapper(response);
//        }
        request=new RequestWrapper(request);
        response = new ResponseWrapper(response);
        long startTime = System.currentTimeMillis();
        try {
            filterChain.doFilter(request, response);
        } finally {
            boolean allowFlag = false;
            if (allow.size() > 0) {
                for (String s : allow) {
                    if (antPathMatcher.match(s, request.getRequestURI())) {
                        allowFlag = true;
                        break;
                    }
                }
            }
            if (expose.size() > 0) {
                for (String s : expose) {
                    if (antPathMatcher.match(s, request.getRequestURI())) {
                        allowFlag = false;
                        break;
                    }
                }
            }
            if (allowFlag) {
                JSONObject jsonObject = traceLogService.traceLog(startTime, request, response);
                if (jsonObject != null) {
                    log.info("Http trace log: {}", JSON.toJSONString(jsonObject, true));
                }
            }
//            updateResponse(response);

        }
    }
    void updateResponse(HttpServletResponse response) throws IOException {
        ContentCachingResponseWrapper responseWrapper = WebUtils.getNativeResponse(response, ContentCachingResponseWrapper.class);
        Objects.requireNonNull(responseWrapper).copyBodyToResponse();
    }
    private HttpTraceLogService traceLogService = new HttpTraceLogService() {
        @Override
        public JSONObject traceLog(long startTime, HttpServletRequest request, HttpServletResponse response) throws IOException {
            if (!isBinaryContent(response)) {
                String path = request.getRequestURI();
                JSONObject jsonObject = new JSONObject();
                jsonObject.put("path", path);
                jsonObject.put("method", request.getMethod());
                jsonObject.put("contentType", request.getContentType());
                jsonObject.put("status", response.getStatus());
                jsonObject.put("parameterMap", request.getParameterMap());
                if (!isMultipart(request)) {
                    jsonObject.put("requestBody", getRequestBody(request));
                    jsonObject.put("responseBody", getResponseBody(response));
                }
                long endTime=System.currentTimeMillis();
                long latency = endTime- startTime;
                jsonObject.put("startTime", DateUtil.format(new Date(startTime), "yyyy-MM-dd HH:mm:ss.SSS"));
                jsonObject.put("endTime",DateUtil.format(new Date(endTime), "yyyy-MM-dd HH:mm:ss.SSS"));
                jsonObject.put("timeToken", latency);
                return jsonObject;
            }
            return null;
        }
    };
}